/*********************************************************/
/**					schedule.c by doom119				 */
/**						April, 2010						 */
/*********************************************************/
#include "task.h"
#include "system.h"
#include "mm.h"
#include "io.h"

#define LATCH (1193180/100)
extern void timer_interrupt();
extern int system_call();


static long user_stack[PAGE_SIZE>>2];
struct {
	long *a;
	short b;
}stack_start = { &user_stack[PAGE_SIZE>>2], 0x10 };

union union_task
{
	struct task task;
	char stack[PAGE_SIZE];
};

static union union_task task0 = {
{
	0, 15, 15, 0, 0, //state, counter, priority, signal, blocked
	0, 0, 0, 0, 0, //start_code, end_code, end_data, brk, start_stack
	0, -1, 0, //pid, father, pgrp;
	0, 0, 0, 0, 0, 0, //uid, euid, suid, gid, egid, sgid
	0, 0, //utime, stime
	{
		{0, 0}, //ldt[0]
		{0x9f, 0xc0fa00}, //ldt[1](cs)
		{0x9f, 0xc0f200} //ldt[2](ds&ss)
	},
	{0, PAGE_SIZE+(long)&task0, 0x10, 0, 0, 0, 0, (long)&_pg_dir, 
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 
		_LDT(0), 0x80000000}
}};

struct task *current = &(task0.task);
struct task *task[NR_TASKS] = {&(task0.task),};

long volatile jiffies = 0;
long startup_time = 0;

void schedule(void)
{
	int i, next, c;
	struct task **p;
	while(1)
	{
		i = NR_TASKS;
		c = -1;
		next = 0;
		p = &task[NR_TASKS];
		while(--i)
		{
			if(!*--p)
				continue;
			if((*p)->state == TASK_RUNNING &&
					(*p)->counter > c)
			{
				c = (*p)->counter;
				next = i;
			}
		}
		if(c) break;
		//ignore task0, run task0 only if no excutable task
		for(p = &LAST_TASK; p != &FIRST_TASK; --p)
		{
			if(*p)
				(*p)->counter = ((*p)->counter >> 1)+(*p)->priority;
		}
	}
	switch_to(next);
}

void sleep_on(struct task **p)
{
	struct task *tmp;
	if(!p)
		return;
	if(current == &(task0.task))
		panic("task[0] trying to sleep");

	tmp = *p;
	*p = current;
	current->state = TASK_UNINTERRUPTIBLE;
	schedule();
	*p = tmp;
	if(tmp)
		tmp->state = TASK_RUNNING;
		
}

void wake_up(struct task **p)
{
	if(p && *p)
	{
		(**p).state = TASK_RUNNING;
		*p = 0;
	}
}

void do_timer(long cpl)
{
	if(cpl)
		current->utime++;
	else
		current->stime++;
	if((--current->counter)>0) return;
	current->counter = 0;
	if(!cpl) return;
	schedule();
}

void schedule_init()
{
	__asm__("nop;nop;nop\n\t"
			::"a"(&task0));

	int i;
	struct descriptor *p;

	set_tss_desc(_gdt+FIRST_TSS_ENTRY, &(task0.task.tss));
	set_ldt_desc(_gdt+FIRST_LDT_ENTRY, &(task0.task.ldt));

	p = _gdt + FIRST_TSS_ENTRY + 2;
	for(i = 1; i < NR_TASKS; ++i)
	{
		task[i] = 0;
		p->low = p->high = 0;
		++p;
		p->low = p->high = 0;
		++p;
	}

	ltr(0);
	lldt(0);

	outb_p(0x36, 0x43);
	outb_p(LATCH & 0XFF, 0x40);
	outb(LATCH >> 8, 0x40);

	set_interrupt_gate(0x20, &timer_interrupt);
	outb(inb_p(0x21)&~0x01, 0x21);

	set_user_trap_gate(0x80, &system_call);
	
	printk("** schdule OK *");
}
